#!/usr/bin/python

#
# KconfigSize tool---kconfigsize manager, control the whole test process
# Copyright (C) 2005-2006 NEC-AS/NEC. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GPLv2, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Created by Mingwei Wang <wangmw@sh.necas.nec.com.cn>
# 2006-02-20 Modified by Mingwei Wang <wangmw@sh.necas.nec.com.cn>
# 2006-08-21 Modified by Guohuang Yin <yingh@sh.necas.nec.com.cn>
#              Control the whole test process in the way of assembly line.
#            At last, database file will be generated without any interrupt.
#            Now it also needn't run with root privilege.
#

import os, sys, string
import commands
import getopt

MAJOR_VERSION = 0
MINOR_VERSION = 1

# default Kconfigsize tools bin program name with the relative path to 
# TESTROOT path used by kconfigsize manager
bin_GenConf_old		=	"bin/local/kc-genconf1.bin"
bin_GenConf_new		=	"bin/local/kc-genconf2.bin"
bin_buildk		=	"bin/local/kc-buildk.bin"
bin_sizetest		=	"bin/local/kc-sizetest.bin"
bin_memtest		=	"bin/local/kc-memtest.bin"
bin_binder		=	"bin/local/kc-binder.py"
bin_indiv_old		=	"bin/local/kc-indiv1.py"
bin_indiv_new		=	"bin/local/kc-indiv2.py"
bin_gendb		=	"bin/local/kc-gendb.py"
bin_cate2dict		=	"bin/local/kc-cate2dict.py"


# configure items in main config file
configs = {}

# bottom-up or top-down test? 
# default: top-down test
mode_config_f = 1


def usage(outfile):
	outfile.write("""Usage: %s <test.conf>

Meta-options:
-h, --help            Display this help then exit.
-v, --version         Output version information then exit.
""" % sys.argv[0])


def print_error(message):
	sys.stderr.write("Error: "+message+"\n")
	sys.stderr.flush()


def error_out(message, rcode):
	print_error(message)
	sys.exit(rcode)


def call_cmd_msg(message, command):
	print message
	print command
	rcode = os.system(command)
	if rcode == 0:
		print "#### Done ####\n"
	else:
		error_out("command: %s failed\n" % command, 2)


def call_cmd_nomsg(command):
	rcode = os.system(command)
	if rcode != 0:
		error_out("command: %s failed\n" % command, 2)


# open conf file and read key and value
def conf_read(info_file):
	global configs
	try:
		fl = open(info_file)
	except:
		error_out("Cannot open configuration file %s" % info_file, 3)

	line_no = 0
	for line in fl.readlines():
		line_no += 1
		if line.startswith("#"):
			continue

		# OK, it's not a config, comment.
		# check if it's empty
		if not line.strip():
			continue

		# line better have an equals in it
		# (single line key=value)
		if line.find("=") == -1:
			print_error("Syntax error in config info file %s:\
		Expected '=' at line %d:\n%s" % (info_file, line_no, line))
			continue
	
		(key, value) = line.split('=', 1)
		key = key.strip()
		value = value.strip()
		configs[key] = value
	fl.close()
	keys = configs.keys()
	keys.sort()


# judge whether KERNEL_CONFIG key exists or empty
def judge_mode_config():
	global mode_config_f

	if configs.has_key('KERNEL_CONFIG') == True \
		and configs['KERNEL_CONFIG'] != "":
		mode_config_f = 1
	else:
		mode_config_f = 0


def trans_normpath(key):
	global configs
	if configs[key][0] != '/':
		tmppath = os.path.join(configs['TESTROOT'], configs[key])
		configs[key] = os.path.normpath(tmppath)


# judge exception & set default value & translate all relative paths to absolute paths
def conf_analysor():
	global configs
	# A. the following keys are specified
	# A.1	TESTROOT must be valid
	if configs.has_key('TESTROOT') == False or configs['TESTROOT'] == "" \
		or os.path.isdir(configs['TESTROOT']) == False:
		error_out("TESTROOT key is invalid\n", 1)
	configs['TESTROOT'] = os.path.normpath(configs['TESTROOT'])
	# A.2	the following keys also must be specified
	for key in ['TEST_NAME', 'VERSION', 'ARCH', 'DO_TEST',\
		'TARGET_LIST', 'KERNEL_SOURCE', 'MAKE_TARGET']:
		if configs.has_key(key) == False or configs[key] == "":
			error_out(key+" key is is missing or empty\n", 1)
	# A.3	KERNEL_CONFIG key must be specified in topdown mode
	judge_mode_config()
	if mode_config_f == 1:
		if configs.has_key('KERNEL_CONFIG') == False or \
				configs['KERNEL_CONFIG'] == "":
			error_out("KERNEL_CONFIG is is missing or empty\n", 1)
	# A.4	the following keys also must be specified 
	#		if the following conditions is satisfied.
	tests = configs['DO_TEST'].split()
	if "mem" in tests:
		for key in ['RESET_HELPER', 'MEM_MEASURE', 'INSTALL_IMAGE',\
							'MEM_RESULT_KEY']:
			if configs.has_key(key) == False or configs[key] == "":
				error_out(key+" key is is missing or empty\n", 1)
	# B. the following keys are optional
	# B.1	if keys are missing, set configs[key] ""
	for key in ['MACH', 'RESET_KEY', 'CROSS_COMPILE', \
				'RESET_HELPER_PARAM', 'MEM_MEASURE_PARAM']:
		if configs.has_key(key) == False:
			configs[key] = ""
	# B.2	if keys are missing or empty, set configs[key]="0"
	for key in ['RESET_TIMEOUT', 'RESET_DELAY']:
		if configs.has_key(key) == False or configs[key] == "":
			configs[key] = "0"
	# B.3	if keys are missing or empty, set configs[key]=configs['TESTROOT']
	for key in ['DB_PATH', 'WORK_PATH']:
		if configs.has_key(key) == False or configs[key] == "":
			configs[key] = configs['TESTROOT']
	# C. translate all relative paths to absolute normal paths
	for key in ['WORK_PATH', 'DB_PATH', 'KERNEL_SOURCE']:
		trans_normpath(key)
	trans_normpath('TARGET_LIST')
	if mode_config_f == 1:
		trans_normpath('KERNEL_CONFIG')
	if "mem" in tests:
		for key in ['RESET_HELPER', 'MEM_MEASURE', 'INSTALL_IMAGE']:
			trans_normpath(key)


# config file syntax:
# ------------------------
# empty lines and lines starting with # are ignored
# single-line attributes are:
# key=value
def conf_parser(info_file):
	conf_read(info_file)
	conf_analysor()


def dir_config():
	global TESTROOT
	TESTROOT = configs['TESTROOT']
	test_path = configs['WORK_PATH']+"/"+configs['TEST_NAME']
	cmd="mkdir -p "+test_path
	os.system(cmd)
	kernel_path = test_path+"/kernel"
	cmd="mkdir -p "+kernel_path
	os.system(cmd)
	work_path = test_path+"/work"
	cmd="mkdir -p "+work_path
	os.system(cmd)

	return 0


# copy config/focus list to work dir 
def cp_list_to_work():
	global configs
# cp KERNEL_CONFIG file
	if mode_config_f == 1:
		cmd = "cp "+configs['KERNEL_CONFIG']+"  "
		cmd = cmd+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/"
		call_cmd_nomsg(cmd)
		tmpls1 = configs['KERNEL_CONFIG'].split('/')
		configs['KERNEL_CONFIG'] = tmpls1[-1]
# cp TARGET_LIST file
	cmd = "cp "+configs['TARGET_LIST']+"  "
	cmd = cmd+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/"
	call_cmd_nomsg(cmd)
	tmpls1 = configs['TARGET_LIST'].split('/')
	configs['TARGET_LIST'] = tmpls1[-1]


def config_generator():
	cmd = TESTROOT + "/"
	if mode_config_f == 1:
		cmd = cmd + bin_GenConf_new
	else:
		cmd = cmd + bin_GenConf_old

	if mode_config_f == 1:
		cmd = cmd+" "+configs['WORK_PATH']+"/"+configs['TEST_NAME']\
			+"/work/"+configs['KERNEL_CONFIG']
		cmd = cmd+" -f " +configs['WORK_PATH']+"/"+\
		configs['TEST_NAME']+"/work/"+configs['TARGET_LIST']
	else:
		cmd = cmd+" "+configs['WORK_PATH']+"/"+configs['TEST_NAME']\
			+"/work/"+configs['TARGET_LIST']

	cmd = cmd+" -a "+configs['ARCH']
	cmd = cmd+" -k "+configs['KERNEL_SOURCE']
	cmd = cmd+" -d "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/kernel"
	cmd = cmd+" -m "+configs['WORK_PATH']+"/"+\
		configs['TEST_NAME']+"/work/category"
	cmd = cmd+" -i "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/index"
	cmd = cmd+" -c "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/configs"

	call_cmd_msg("#### start .config generator ####", cmd)


# generater category dictionary
def cate2dict_generator():
	cmd = TESTROOT + "/"
	cmd = cmd+bin_cate2dict+ " -i "+configs['WORK_PATH']+"/"+\
		configs['TEST_NAME']+"/work/category"
	if mode_config_f == 1:
		web_cate_name = configs['TEST_NAME']+"_td.cat"
	else:
		web_cate_name = configs['TEST_NAME']+"_bu.cat"
	cmd = cmd+" -o "+configs['DB_PATH']+"/"+web_cate_name
	call_cmd_nomsg(cmd)


def kernel_builder():
	cmd = TESTROOT + "/" + bin_buildk
	cmd = cmd+" -c "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/configs"
	cmd = cmd+" -k "+configs['KERNEL_SOURCE']
	cmd = cmd+" -t "+configs['MAKE_TARGET']
	cmd = cmd+" -a "+configs['ARCH']
	if configs['CROSS_COMPILE']:
		cmd = cmd + " -x "+configs['CROSS_COMPILE']
	cmd = cmd+" -d "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/kernel"
	cmd = cmd+" -o "+configs['WORK_PATH']+"/"+configs['TEST_NAME']\
			+"/work/build_result"

	call_cmd_msg("#### start kernel builder ####", cmd)


def lists_generator():
	r_file = configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/build_result"
	o_file = configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/mem_kernels"
	try:
		fr = open(r_file)
	except:
		error_out("Cannot open result file %s" % r_file, 3)
	try:
		fo = open(o_file,'w')
	except:
		error_out("Cannot open output file %s" % o_file, 3)

	for line in fr.readlines():
		line = line.strip()
		val = line.split(",")
		fo.write(val[0])
		fo.write(',')
		fo.write(val[2])
		fo.write('\n')
	fo.close()

	cmd = "cp "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/build_result "
	cmd = cmd+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/size_kernels"

	call_cmd_nomsg(cmd)


def size_tester():
	cmd = TESTROOT + "/" + bin_sizetest
	cmd = cmd+" "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/size_kernels"
	cmd = cmd+" -o "+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/size_result"

	call_cmd_msg("#### start size tester ####", cmd)


def record_conditions():
	o_file = configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/conditions"
	try:
		fo = open(o_file,'w')
	except:
		error_out("Cannot open output file %s" % o_file, 3)

	fo.write('KERNEL_VERSION,'+configs['VERSION'])
	fo.write('\n')
	fo.write('ARCH,'+configs['ARCH'])
	fo.write('\n')
	fo.write('MACH,'+configs['MACH'])
	fo.write('\n')
	if mode_config_f == 1:
		fo.write('KERNEL_CONFIG,'+configs['KERNEL_CONFIG'])
		fo.write('\n')
	fo.write('TARGET_LIST,'+configs['TARGET_LIST'])
	fo.write('\n')

	temp_file = '/tmp/.gcc_version.tmp'
	cmd = "gcc -v &> "+temp_file
	call_cmd_nomsg(cmd)
	try:
		ft = open(temp_file)
	except:
		error_out("Cannot open temp file %s" % temp_file, 3)
	for linet in ft.readlines():
		linet = linet.strip()
	val = linet.split(" ")

	version=val[2]
	fo.write('GCC_VERSION,'+version)
	fo.write('\n')

	temp_file = '/tmp/.binutils_version.tmp'
	cmd = "ld -v &> "+temp_file
	call_cmd_nomsg(cmd)
	try:
		ft = open(temp_file)
	except:
		error_out("Cannot open temp file %s" % temp_file, 3)
	for linet in ft.readlines():
		linet = linet.strip()
	val = linet.split(" ")

	version=val[3]
	fo.write('BINUTILS_VERSION,'+version)
	fo.write('\n')
	fo.close()


def mem_config():
	o_file = configs['TESTROOT'] + "/conf/memtest.conf"
	print "#### setting %s ####" % o_file 
	try:
		fo = open(o_file,'w')
	except:
		error_out("Cannot open output file %s" % o_file, 3)

	fo.write('# Automatically generated by ' + sys.argv[0] + '\n')
	fo.write('# Edit carefully!\n\n')
	fo.write('RESET_HELPER='+configs['RESET_HELPER'])
	fo.write('\n')
	fo.write('RESET_HELPER_PARAM='+configs['RESET_HELPER_PARAM'])
	fo.write('\n')
	if configs.has_key('RESET_KEY') == True:
		fo.write('RESET_KEY='+configs['RESET_KEY'])
		fo.write('\n')
	fo.write('RESET_TIMEOUT='+configs['RESET_TIMEOUT'])
	fo.write('\n')
	if configs.has_key('RESET_DELAY') == True:
		fo.write('RESET_DELAY='+configs['RESET_DELAY'])
		fo.write('\n')
	fo.write('MEM_MEASURE='+configs['MEM_MEASURE'])
	fo.write('\n')
	fo.write('MEM_MEASURE_PARAM='+configs['MEM_MEASURE_PARAM'])
	fo.write('\n')
	fo.write('MEM_RESULT_KEY='+configs['MEM_RESULT_KEY'])
	fo.write('\n')
	fo.write('IMAGE_LIST='+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/mem_kernels")
	fo.write('\n')
	fo.write('INSTALL_IMAGE='+configs['INSTALL_IMAGE'])
	fo.write('\n')
	fo.write('RESULT='+configs['WORK_PATH']+"/"+configs['TEST_NAME']+"/work/mem_result")
	fo.write('\n')
	fo.close()


def mem_tester():
	mem_config()
	cmd = TESTROOT + "/" + bin_memtest
	cmd = cmd + " -c " + configs['TESTROOT'] + "/conf/memtest.conf"

	call_cmd_msg("#### start mem tester ####", cmd)


def size_binder_indiv():
	cmd = TESTROOT + "/" + bin_binder + " -t size -d "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work/binder_size_result"

	call_cmd_msg("#### start size binder ####", cmd)

	if mode_config_f == 1:
		bin_indiv = bin_indiv_new
	else:
		bin_indiv = bin_indiv_old

	cmd = TESTROOT + "/" + bin_indiv+ " -t size "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work/binder_size_result "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work/indiv_size_result"

	call_cmd_msg("#### start size indiv ####", cmd)


def mem_binder_indiv():
	cmd = TESTROOT + "/" + bin_binder + " -t mem -d "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work/binder_mem_result"

	call_cmd_msg("#### start mem binder ####", cmd)

	if mode_config_f == 1:
		bin_indiv = bin_indiv_new
	else:
		bin_indiv = bin_indiv_old

	cmd = TESTROOT + "/" + bin_indiv+ " -t mem "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work/binder_mem_result "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work/indiv_mem_result"

	call_cmd_msg("#### start mem indiv ####", cmd)


def db_generator():
	cmd = TESTROOT + "/" + bin_gendb + " -d "
	cmd = cmd + configs['WORK_PATH'] + "/" + configs['TEST_NAME'] + "/work "
	cmd = cmd + "-o " + configs['DB_PATH'] + "  "
	if mode_config_f == 1:
		db_name = configs['TEST_NAME']+"_td.db"
	else:
		db_name = configs['TEST_NAME']+"_bu.db"

	cmd = cmd + db_name 
	call_cmd_msg("#### start db generator ####", cmd)


def kconfigweb_config():
	o_file = configs['TESTROOT'] + "/conf/kconfigweb.conf"
	if os.path.isfile(o_file) == True:
		o_file = o_file + ".tmp"
	print "#### setting %s ####" % o_file 
	try:
		fo = open(o_file,'w')
	except:
		error_out("Cannot open output file %s" % o_file, 3)

	fo.write('# Automatically generated by ' + sys.argv[0] + '\n')
	fo.write('# Edit carefully!\n\n')
	fo.write('# Database configuation\n')
	fo.write('# database file directory with an abolute path.\n')
	fo.write('db_dir=' + configs['DB_PATH'])
	fo.write('\n')

	fo.close()


def main(argv = None):

	if argv is None:
		argv = sys.argv
	try:
		opts, prog_argv = getopt.getopt(argv[1:], "hv",
					["help", "version",])
	except getopt.error, msg:
		sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
		sys.stderr.write("Try `%s --help' for more information\n"
				 % sys.argv[0])
		sys.exit(1)

	for opt, val in opts:
		if opt == "-h" or opt == "--help":
			usage(sys.stdout)
			sys.exit(0)

		if opt == "-v" or opt == "--version":
			sys.stdout.write("test_manager version %d.%d\n" % (MAJOR_VERSION, MINOR_VERSION))
			sys.exit(0)

		assert 0, "Should never get here"

	if len(prog_argv) == 1:
		config_file = prog_argv[0]
	else:
		sys.stderr.write("config file?\n")
		sys.stderr.write("Try `%s --help' for more information\n"
				 % sys.argv[0])
		sys.exit(1)

# read config file and do configuration
	conf_parser(config_file)
	dir_config()
	cp_list_to_work()
	record_conditions()
# generate linux kernel .config files
	config_generator()
# builder linux kernel one by one according to .config files
	kernel_builder()
# generate image list files for size test and mem test
	lists_generator()
# do size or/and mem test according to DO_TEST settings 
	tests = configs['DO_TEST'].split()
	for val in tests:
		if val == 'size':
			size_tester()
		if val == 'mem':
			mem_tester()
# binder and indiv according to DO_TEST settings
	for val in tests:
		if val == 'size':
			size_binder_indiv()
		if val == 'mem':
			mem_binder_indiv()
# generate db file
	db_generator()
# generate category dictionary to accelerate web access
	cate2dict_generator()
# generate systemsize configure file
	kconfigweb_config()

	sys.exit(0)
if __name__=="__main__":
	main()
